package core;

import core.entities.Card;
import core.entities.Location;

/**
 * Superclass modeling the behaviour of a player.<BR/>
 * The player can be physical or not (artificial intelligence)
 * 
 * @author Alexandre Boursier
 * @version 1.0.0
 * 
 */
public abstract class Player {

	/** The current deck of the player (cards turn over ready to be shown) */
	private Deck deck;
	/** The discarded cards stack */
	private PlayedCards playedCards;
	
	/**
	 * Standard constructor
	 */
	public Player() {
		deck = new Deck();
		playedCards = new PlayedCards();
	}

	/**
	 * First constructor
	 * 
	 * @param deck
	 *            the deck created at the beginning
	 * 
	 */
	public Player(Deck deck) {
		this.deck = deck;
		this.playedCards = new PlayedCards();
	}

	/**
	 * Second constructor
	 * 
	 * @param deck
	 *            the deck created at the beginning
	 * @param played
	 *            the current played cards
	 * 
	 */
	public Player(Deck deck, PlayedCards played) {
		this.deck = deck;
		this.playedCards = played;
	}

	/**
	 * @return the deck
	 */
	public Deck getDeck() {
		return deck;
	}

	/**
	 * @param deck
	 *            the deck to set
	 */
	public void setDeck(Deck deck) {
		this.deck = deck;
	}

	/**
	 * @return the playedCards
	 */
	public PlayedCards getPlayedCards() {
		return playedCards;
	}

	/**
	 * @param playedCards
	 *            the playedCards to set
	 */
	public void setPlayedCards(PlayedCards playedCards) {
		this.playedCards = playedCards;
	}

	/**
	 * Clear the deck of a player
	 */
	public void clearDeck() {
		this.getDeck().getCards().clear();
	}

	/**
	 * Clear the played cards of a player
	 */
	public void clearPlayedCards() {
		this.getPlayedCards().getCards().clear();
	}

	/**
	 * Clears all the cards of the player
	 */
	public void clearAll() {
		clearDeck();
		clearPlayedCards();
	}
	
	/**
	 * addCardToDeck method<BR />
	 * Card giving method. This method puts a card
	 * within the player's deck. After that the deck
	 * is shuffled.
	 * 
	 * @param card the card to be given to the player.
	 */
	public void addCardToDeck(Card card) {
		if (card == null)
			throw new NullPointerException("Given card is null");
		if (deck == null)
			throw new NullPointerException("Deck is null, card cannot be given");
		deck.addCard(card);
		deck.shuffle();
	}

	/**
	 * Play a new card and put it on the top of the played cards list
	 * 
	 * @return the played card
	 */
	public Card playCard() {
		Card card = deck.getTopCard();
		playedCards.addCard(card);
		return card;
	}
	
	/**
	 * playerSpecificReaction method<BR />
	 * This method is called on each turn, its
	 * results depend on the player's type.
	 */
	public abstract void playerSpecificReaction();
	
	/**
	 * playerWinReaction method<BR />
	 * This method is called when the player have win, its
	 * results depend on the player's type.
	 */
	public abstract void playerWinReaction(Location loc);
	
	/**
	 * updateStats method<BR />
	 * Stats updating method. This method is called
	 * each time a player asks his profile to update
	 * his stats.<BR />
	 * This method is overridden by HumanPlayers notably.
	 */
	abstract public void updateStats();
	
	/**
	 * generateScore method<BR />
	 * Score generation method. This method is called
	 * when the game has ended and returns the score
	 * this player has made for the current game.<BR />
	 * This method is overridden by HumanPlayers notably.
	 * 
	 * @return The score this player has made.
	 */
	abstract public Score generateScore();

	/**
	 * giveCards method<BR />
	 * This method adds the cards given in parameter
	 * to the deck of this player. It represents the
	 * moment when a player loses a confrontation.
	 * @param playedCards
	 */
	public void receiveCards(PlayedCards playedCards) {
		deck.addCards(playedCards);
	}

	/**
	 * prompt method<BR />
	 * This method is called on confrontations, it asks
	 * the player for a reaction and measures his reaction
	 * time.
	 * @return the player's reaction time after the prompt
	 */
	abstract public double prompt(Location loc);

	/**
	 * hasNoMoreCards method<BR />
	 * This method allows to know if a player has emptied
	 * both his deck and his played cards.
	 * @return <code>true</code> if both deck and playedCards are empty, <code>false</code> otherwise.
	 */
	public boolean hasNoMoreCards() {
		return (deck.isEmpty() & playedCards.isEmpty());
	}

	public Card getLastPlayedCard() {
		return playedCards.getLastPlayedCard();
	}

	public boolean isDeckEmpty() {
		return deck.isEmpty();
	}
}
